package com.ruoyi.lock.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.lock.domain.ADevice;
import com.ruoyi.lock.domain.APeriodicPassword;
import com.ruoyi.lock.domain.OfflinePassword;
import com.ruoyi.lock.domain.vo.PasswordGenerationVo;
import com.ruoyi.lock.service.IADeviceService;
import com.ruoyi.lock.service.IAPeriodicPasswordService;
import com.ruoyi.lock.service.OfflinePasswordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 离线密码服务实现
 * 
 * 注意：周期性密码管理已从Redis缓存迁移到MySQL数据库存储
 * 修改内容：
 * 1. 移除了周期性密码Redis缓存相关代码
 * 2. 移除了Redis序列号管理相关方法
 * 3. 使用固定序列号1替代动态序列号
 * 4. 保留数据库存储功能
 */
@Service
@Slf4j
public class OfflinePasswordServiceImpl implements OfflinePasswordService {

    @Autowired
    private IADeviceService iaDeviceService;
    
    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IAPeriodicPasswordService aPeriodicPasswordService;
    
    // Redis键前缀，用于区分不同MAC地址的计数器
    private static final String REDIS_KEY_PREFIX = "lock:password:counter:";
    // 计数器最大值
    private static final int MAX_COUNTER = 99;
    // 计数器过期时间（天）
    private static final int COUNTER_EXPIRE_DAYS = 90;
    
    @Override
    public AjaxResult passwordGeneration(PasswordGenerationVo passwordGenerationVo) {
        ADevice aDevice = iaDeviceService.selectADeviceListByHomeId(passwordGenerationVo.getHomeId());
        if (aDevice == null) {
            return AjaxResult.error("找到不到房间!");
        }
        
        // 根据密码类型生成不同的密码
        switch (passwordGenerationVo.getType()) {
            case 1:
                // 一次性密码
                return AjaxResult.success(this.generateOneTimePassword(
                    aDevice.getMac(), 
                    aDevice.getSecKey().toString(),
                    LocalDateTime.now()
                ));
            case 2:
                // 周期性密码
                if (passwordGenerationVo.getStartTime() == null || passwordGenerationVo.getEndTime() == null) {
                    return AjaxResult.error("生成周期性密码需要提供开始和结束时间");
                }
                
                LocalDateTime startTime = LocalDateTime.parse(passwordGenerationVo.getStartTime());
                LocalDateTime endTime = LocalDateTime.parse(passwordGenerationVo.getEndTime());
                
                // 验证结束时间不能小于开始时间
                if (endTime.isBefore(startTime)) {
                    return AjaxResult.error("结束时间不能早于开始时间");
                }
                
                return AjaxResult.success(this.generatePeriodPassword(
                    startTime, 
                    endTime,
                    aDevice.getMac(), 
                    aDevice.getSecKey().toString(),
                    1, // 固定使用序列号1
                    passwordGenerationVo.getHomeId(),
                    passwordGenerationVo.getRemarks()
                ));
            case 3:
                // 永久密码
                return AjaxResult.success(this.generateLongTimePassword(
                    LocalDateTime.now(),
                    aDevice.getMac(), 
                    aDevice.getSecKey().toString(),
                    1
                ));
            default:
                return AjaxResult.error("不支持的密码类型");
        }
    }

    /**
     * ### 密码生成 SDK 调用方法
     * 参数说明：
     * LockClient 构建需要两个参数，一个是：门锁mac地址（带冒号），一个是门锁密钥，为16进制字符串；
     *
     * 1. 一次性密码 需要传入开始时间和密码序号，密码序号支持0～99，既：同一个开始时间允许生成100个密码
     */

    // 生成一次性密码
    private OfflinePassword generateOneTimePassword(String mac, String secret, LocalDateTime startTime) {
        // 获取该MAC地址对应的计数器值
        int serialNumber = getAndIncrementCounter(mac);
        
        log.info("为MAC: {} 生成一次性密码，使用序列号: {}", mac, serialNumber);
        
        try {
            com.smartlock.iot.LockClient.OfflinePassword timePassword = com.smartlock.iot.LockClient
                    .build(mac, secret)
                    .generateOneTimePassword(startTime, serialNumber);
            
            if (timePassword != null) {
                return JSONObject.parseObject(JSON.toJSONString(timePassword), OfflinePassword.class);
            }
        } catch (Exception e) {
            log.error("生成一次性密码失败, MAC: {}, 序列号: {}, 错误: {}", mac, serialNumber, e.getMessage(), e);
        }
        
        return null;
    }
    
    /**
     * 获取并递增MAC地址对应的计数器值
     * 
     * @param mac 门锁MAC地址
     * @return 当前计数器值（1-99）
     */
    private int getAndIncrementCounter(String mac) {
        String redisKey = REDIS_KEY_PREFIX + mac;
        
        // 获取当前计数器值，如果不存在则初始化为1
        Integer counter = redisCache.getCacheObject(redisKey);
        if (counter == null || counter < 1 || counter > MAX_COUNTER) {
            counter = 1;
        } else {
            // 递增计数器
            counter++;
            if (counter > MAX_COUNTER) {
                counter = 1; // 重置为1，循环使用
            }
        }
        
        // 保存更新后的计数器值
        redisCache.setCacheObject(redisKey, counter, COUNTER_EXPIRE_DAYS, TimeUnit.DAYS);
        
        return counter;
    }

    /**
     * 周期密码
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param mac 门锁mac地址 (16进制字符串)
     * @param secret 门锁密钥
     * @param serialNumber 密码序号支持0～9，既：同一开始/结束时间支持生成10个密码
     * @param homeId 房间ID
     * @param remarks 备注信息
     */
    private OfflinePassword generatePeriodPassword(LocalDateTime startTime, LocalDateTime endTime, String mac, String secret, int serialNumber, Long homeId, String remarks) {
        // 先查询数据库中是否已存在相同参数的密码
        APeriodicPassword queryObj = new APeriodicPassword();
        queryObj.setDeviceMac(mac);
        queryObj.setStartTime(startTime);
        queryObj.setEndTime(endTime);
        queryObj.setSerialNumber(serialNumber);
        queryObj.setHomeId(homeId);
        
        List<APeriodicPassword> existingPasswords = aPeriodicPasswordService.selectAPeriodicPasswordList(queryObj);
        if (existingPasswords != null && !existingPasswords.isEmpty()) {
            APeriodicPassword existingPassword = existingPasswords.get(0);
            log.info("找到已存在的周期性密码, MAC: {}, 开始时间: {}, 结束时间: {}, 序列号: {}", mac, startTime, endTime, serialNumber);
            
            // 将已存在的密码转换为OfflinePassword对象返回
            OfflinePassword password = new OfflinePassword();
            password.setPassword(existingPassword.getPassword());
            password.setStartTime(existingPassword.getStartTime().toString());
            password.setEndTime(existingPassword.getEndTime().toString());
            return password;
        }
        
        // 数据库中不存在，生成新密码
        log.info("生成新的周期性密码, MAC: {}, 开始时间: {}, 结束时间: {}, 序列号: {}", mac, startTime, endTime, serialNumber);
        
        try {
            com.smartlock.iot.LockClient.OfflinePassword timePassword = com.smartlock.iot.LockClient
                    .build(mac, secret)
                    .generatePeriodPassword(startTime, endTime, serialNumber);
            
            if (timePassword != null) {
                OfflinePassword password = JSONObject.parseObject(JSON.toJSONString(timePassword), OfflinePassword.class);
                
                // 保存到数据库
                APeriodicPassword dbPassword = new APeriodicPassword();
                dbPassword.setDeviceMac(mac);
                dbPassword.setPassword(password.getPassword());
                dbPassword.setStartTime(startTime);
                dbPassword.setEndTime(endTime);
                dbPassword.setSerialNumber(serialNumber);
                dbPassword.setHomeId(homeId);
                dbPassword.setRemarks(remarks);
                dbPassword.setIsExpired(false);
                aPeriodicPasswordService.insertAPeriodicPassword(dbPassword);
                
                return password;
            }
        } catch (Exception e) {
            log.error("生成周期性密码失败, MAC: {}, 序列号: {}, 错误: {}", mac, serialNumber, e.getMessage(), e);
        }
        
        return null;
    }
    
    /**
     * 长期密码，需要传入开始时间和密码序号，密码序号支持0～9，既：同一开始时间支持生成10个密码
     * @param startTime 开始时间
     * @param mac 门锁mac地址 (16进制字符串)
     * @param secret 门锁密钥
     * @param serialNumber 密码序号支持0～9，既：同一开始时间支持生成10个密码
     */
    private OfflinePassword generateLongTimePassword(LocalDateTime startTime,String mac, String secret,int serialNumber) {
        com.smartlock.iot.LockClient.OfflinePassword timePassword = com.smartlock.iot.LockClient
                .build(mac, secret)
                .generateLongTimePassword(startTime, serialNumber);
        if (timePassword != null) {
            return JSONObject.parseObject(JSON.toJSONString(timePassword), OfflinePassword.class);
        }
        return null;
    }

    //转16进制
    public String convertToHex(String str) {
        StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            hexString.append(Integer.toHexString((int) c));
        }
        return hexString.toString();
    }

}
